/**
 * Copyright 2008-2009. Chongqing Communications Industry Services Co.,Ltd Information Technology Branch. All rights
 * reserved. <a>http://www.cqcis.com</a>
 */
package com.cqcis.uip.base.framework.codec.text;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.cqcis.uip.base.framework.FrameworkConstants;
import com.cqcis.uip.base.framework.codec.ObjectDecoder;
import com.cqcis.uip.base.framework.common.ControlMsg;
import com.cqcis.uip.base.framework.common.TelnetCommand;
import com.cqcis.uip.base.framework.utils.ByteBuffer;
import com.cqcis.uip.base.framework.utils.StringUtil;

/**
 * telnet协议解码器实现
 * 
 * @author huangmy
 * @date   2008-8-9
 */
public class TextProtocolDecoder implements ObjectDecoder {
	private static Log log = LogFactory.getLog(TextProtocolDecoder.class);
	private final static int MIN_CMD_LENGTH = 3; // 命令体的最小长度	
	private final static String TEXT_BYTEBUFFER = "TEXT_BYTEBUFFER" + TextProtocolDecoder.class.getName();
	private final static String LINE_BYTEBUFFER = "LINE_BYTEBUFFER" + TextProtocolDecoder.class.getName();
	private String charset = "GBK"; // 默认用GBK, 同时兼容英文、简体、繁体
	private String[] linePrompts; // 行提示符
	private boolean printLog = true;
    
	/**
	 * 字符缓冲
	 * 
	 * @param buffer
	 * @return
	 */
	private ByteBuffer getTextBuffer(ByteBuffer buffer) {
		ByteBuffer textBuffer = (ByteBuffer)buffer.getSession().getAttribute(TEXT_BYTEBUFFER);
		if (textBuffer == null) {
			textBuffer = ByteBuffer.allocate(80).setAutoExpand(true);
			buffer.getSession().setAttribute(TEXT_BYTEBUFFER, textBuffer);
		}
		
		return textBuffer;
	}
	
	/**
	 * 行缓冲
	 * 
	 * @param buffer
	 * @return
	 */
	private ByteBuffer getLineBuffer(ByteBuffer buffer) {
		ByteBuffer lineBuffer = (ByteBuffer)buffer.getSession().getAttribute(LINE_BYTEBUFFER);
		if (lineBuffer == null) {
			lineBuffer = ByteBuffer.allocate(80).setAutoExpand(true);
			buffer.getSession().setAttribute(LINE_BYTEBUFFER, lineBuffer);
		}
		
		return lineBuffer;
	}
	
	/**
	 * 读取一个命令
	 * 
	 * @param buffer
	 * @return
	 */
	private TelnetCommand readCommand(ByteBuffer buffer) {		
		byte c = buffer.get(); // 命令            	
    	byte p = buffer.get(); // 参数, 即选项
    	
    	TelnetCommand cmd = new TelnetCommand(c,p,null);
    	
    	if (c == TelnetCommand.SB) {// 如果是子命令协商开始    	
    		cmd = new TelnetCommand(TelnetCommand.SB,p,null);
    		ByteBuffer tmpBuf = ByteBuffer.allocate(32).setAutoExpand(true);    		
    		tmpBuf.put(p);
    		// 开始读取子命令内容
    		while (buffer.hasRemaining()) {
    			byte n = buffer.get();
    			if (n == TelnetCommand.IAC) {
    				if (!buffer.hasRemaining()) {
    					return null;
    				}
					n = buffer.get();
					if (n == TelnetCommand.SE) {// 子命令结束
						tmpBuf.flip();
						cmd.setSubCmdContext(tmpBuf.array());
						break;
					} else { 
						tmpBuf.put(n);
					}
				} else {
					tmpBuf.put(n);
				}
    		}
    		if (cmd.getSubCmdContext() == null) {// 没有子命令
				return null;
			}    		
    	}
    	
    	return cmd; // 取得一个命令
	}
	
	public Object decode(ByteBuffer buffer) throws Exception {		
		if (buffer.getSession().getAttribute(FrameworkConstants.TELNETFIRSTSEND_03) == null) {			
			return null; // 异步接受数据类型接口一般要进行该判断，如果还没发第一次数据，先不接受
		}
		
		if (buffer.remaining() <= 0) { // 命令体的最小长度
			return null;
		}
		
		int oldpos = buffer.position();
		byte b = buffer.get();
		if (b == TelnetCommand.IAC) {// telnet命令开始			
			if (buffer.remaining() <  MIN_CMD_LENGTH -1) {
				return null;
			}
        	// 读一个命令
        	return toControlMsg(readCommand(buffer));
        }
		buffer.position(oldpos);
		
		// 缓冲字符串 buffer
		ByteBuffer textBuffer = getTextBuffer(buffer);		
		ByteBuffer lineBuffer = getLineBuffer(buffer);
		
		lineBuffer.clear();
		textBuffer.clear();// 先清空临时缓冲
		int linecount = 0;
		int lineEndpos = 0;
		
		// 找到行尾位置        
        while (buffer.hasRemaining()) {
            b = buffer.get();
            if (b == TelnetCommand.IAC) {// telnet命令开始
            	buffer.position(buffer.position() - 1);
            	// 接下来的字节是命令, 先不处理,把现有的数据东东返回
            	lineEndpos = buffer.position();
        		linecount ++;
        		textBuffer.flip();
            	lineBuffer.put(textBuffer);
            	textBuffer.clear();
            	
            	break;
            }
            
            textBuffer.put(b);
            if (b == '\r') {
            	if (buffer.hasRemaining()) {
            		byte d = buffer.get();            		
            		if (d == '\n' || d == 0x00) { // UNIX 行尾, 读出一行到缓冲字符buffer中
            			textBuffer.put((byte)'\n');
                    	lineEndpos = buffer.position();
                    	linecount ++;
                    	textBuffer.flip();
                    	lineBuffer.put(textBuffer);
                    	textBuffer.clear();
                    } else {
                    	textBuffer.put(d);
                    }
            	}
            }
        }
        
        // 定位到本行地址开始
        if (linecount > 0) {
        	buffer.position(lineEndpos);
        	lineBuffer.flip();        	
        	return toControlMsg(getStringFromBuf(lineBuffer));
        } else {
        	textBuffer.flip();
        	String s = getStringFromBuf(textBuffer);        	
        	for (String t : this.linePrompts) {
        		s = StringUtil.trimWhiteToNull(s);
        		if (s != null && s.endsWith(t)) {        			
            		return toControlMsg(s);
            	}
        	}	
        }
        
		return null;
	}
	
	/**
	 * 控制信息
	 * 
	 * @param s
	 * @return
	 */
	private Object toControlMsg(String s) {		
		return new ControlMsg(s);
	}
	
	/**
	 * 控制信息
	 * 
	 * @param s
	 * @return
	 */
	private Object toControlMsg(TelnetCommand cmd) {
		ControlMsg msg = new ControlMsg();		
		msg.setAttachement(cmd); // 挂载命令	
		
		if(log.isDebugEnabled() && printLog) {
			log.debug("读出选项协商控制: " + cmd);
		}
				
		return msg;
	}
	
	/**
	 * 获取字符串
	 * 
	 * @param buf
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	private String getStringFromBuf(ByteBuffer buf) throws UnsupportedEncodingException {		
		return new String(buf.array(), this.charset); 
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

	public void setLinePrompt(List<String> list) {
		List<String> promptsList = new ArrayList<String>();
		for (String linePrompt : list) {
			String[] prompts = linePrompt.split(",");
			if (prompts != null) {
				for (String s : prompts) {
					promptsList.add(s);					
				}
			}
		}
				
		this.linePrompts = promptsList.toArray(new String[promptsList.size()]);
	}

	public void setPrintLog(boolean printLog) {
		this.printLog = printLog;
	}
}
